/*
 * Copyright (C) 2011, EADS France
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.interpreter.backends.rete;

import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Laurent WOUTERS
 */
public class BetaNCCExitNode implements TokenHolder, TokenActivable {
    private boolean isInTransaction;
    private TokenHolder betaMem;
    private TokenActivable child;
    private Map<Token, List<Token>> matches;
    
    @Override public Collection<Token> getTokens() { return betaMem.getTokens(); }
    @Override public Collection<TokenActivable> getChildren() {
        Collection<TokenActivable> list = new ArrayList<TokenActivable>();
        if (child != null)
            list.add(child);
        return list;
    }
    @Override public void addChild(TokenActivable activable) { this.child = activable; }
    @Override public void removeChild(TokenActivable activable) { this.child = null; }
    @Override public void removeAllChildren() { this.child = null; }
    
    public BetaNCCExitNode(TokenHolder beta) {
        this.isInTransaction = false;
        this.betaMem = beta;
        this.matches = new IdentityHashMap<Token, List<Token>>();
    }
    
    @Override public void activateToken(Token token) {
        Token original = getOriginal(token);
        matches.get(original).add(token);
        if (!isInTransaction)
            child.deactivateToken(original);
    }
    @Override public void deactivateToken(Token token) {
        Token original = getOriginal(token);
        List<Token> sm = matches.get(original);
        sm.remove(token);
        if (!isInTransaction && sm.isEmpty())
            child.activateToken(original);
    }
    @Override public void activateTokens(Collection<Token> tokens) {
        Token[] buffer = new Token[tokens.size()];
        int i = 0;
        for (Token token : tokens) {
            Token original = getOriginal(token);
            matches.get(original).add(token);
            buffer[i] = original;
            i++;
        }
        if (!isInTransaction)
            child.deactivateTokens(new FastBuffer<Token>(buffer));
    }
    @Override public void deactivateTokens(Collection<Token> tokens) {
        Collection<Token> buffer = new ArrayList<Token>();
        for (Token token : tokens) {
            Token original = getOriginal(token);
            List<Token> sm = matches.get(original);
            sm.remove(token);
            if (!isInTransaction && sm.isEmpty())
                buffer.add(original);
        }
        if (!buffer.isEmpty())
            child.activateTokens(new FastBuffer<Token>(buffer));
    }
    
    public void preActivation(Token token) {
        matches.put(token, new ArrayList<Token>());
        isInTransaction = true;
    }
    public void postActivation(Token token) {
        isInTransaction = false;
        if (matches.get(token).isEmpty())
            child.activateToken(token);
    }
    public void preActivation(Collection<Token> tokens) {
        for (Token token : tokens)
            matches.put(token, new ArrayList<Token>());
        isInTransaction = true;
    }
    public void postActivation(Collection<Token> tokens) {
        isInTransaction = false;
        Iterator<Token> iter = tokens.iterator();
        while (iter.hasNext()) {
            Token token = iter.next();
            if (!matches.get(token).isEmpty())
                iter.remove();
        }
        if (!tokens.isEmpty())
            child.activateTokens(tokens);
    }
    
    public void preDeactivation(Token token) {
        isInTransaction = true;
    }
    public void postDeactivation(Token token) {
        matches.remove(token);
        child.deactivateToken(token);
    }
    public void postDeactivation(Collection<Token> tokens) {
        for (Token token : tokens)
            matches.remove(token);
        child.deactivateTokens(tokens);
    }
    
    private Token getOriginal(Token token) {
        while (token != null) {
            if (matches.containsKey(token))
                return token;
            token = token.getParent();
        }
        return null;
    }
}
